#include "logcplus_writer_remote_udp.h"

namespace logcplus
{
    #define UDP_CMD_BEGIN  "1"
    #define UDP_CMD_WRITE  "2"
    #define UDP_CMD_COMMIT "3"

    #define maxBufferSize       8192

    typedef struct remote_request_header
    {
        char xCommond[2];
        char xHostName[30];
        char xLogName[20];
        char xLogNbr[30];
        char xLength[8];
    }remote_request_header_t;

    socket_client::socket_client()
        : m_sockFd(-1)
        , m_port(-1)
        , m_isHeader(false)
    {
    }

    socket_client::~socket_client()
    {
        disConnect();	
    }

    int socket_client::connect(const char* psIPAddr,int port)
    {
        disConnect();
        
        m_sockFd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
        if(m_sockFd < 0)
        {
            perror("socket error");
            return -1;
        }
        m_port = port;	
        m_sockAddr.sin_family = AF_INET;
        m_sockAddr.sin_port = htons(m_port);
        if(inet_pton(AF_INET, psIPAddr, &m_sockAddr.sin_addr) <= 0)
        {
            perror("inet_pton error");
            return -2;
        }	
        return 0;
    }

    int socket_client::disConnect()
    {
        if(m_sockFd > 0)
        {
            close(m_sockFd);
            m_sockFd = -1;
        }
        return 0;
    }

    int socket_client::begin(const char* psCDC,const char* psLogSeqno,const char* psHostName)
    {
        if(!m_isHeader)
        {   
            return 0;
        }
        char sLogSendBuff[maxBufferSize]={0};
        char* lpBuff = sLogSendBuff;
        struct remote_request_header xLogHeader;
        memset(&xLogHeader,' ',sizeof(struct remote_request_header));

        memcpy(xLogHeader.xCommond,UDP_CMD_BEGIN,1);
        memcpy(xLogHeader.xLogName,psCDC,strlen(psCDC));
        memcpy(xLogHeader.xLogNbr,psLogSeqno,strlen(psLogSeqno));
        memcpy(xLogHeader.xHostName,psHostName,strlen(psHostName));

        char sLogTextLen[10];
        sprintf(sLogTextLen,"%08d",0);
        memcpy(xLogHeader.xLength,sLogTextLen,strlen(sLogTextLen));

        memcpy(lpBuff,&xLogHeader,sizeof(struct remote_request_header));

        return send_request(lpBuff);
    }

    int socket_client::write(const char* psCDC,const char* psLogSeqno,const char* psHostName,const char* psLogText)
    {
        int iRet = 0;
        char sLogSendBuff[maxBufferSize]={0};
        char* lpBuff = sLogSendBuff;
        char* pNewBuf = nullptr;
        int nHeadLen = 0;

        int nLength =strlen(psLogText);
        if (nLength > sizeof(sLogSendBuff))
        {
            pNewBuf = new char[nLength+1];
            lpBuff = pNewBuf;
        }        

        if(m_isHeader)
        {   
            struct remote_request_header xLogHeader;
            memset(&xLogHeader,' ',sizeof(struct remote_request_header));

            memcpy(xLogHeader.xCommond,UDP_CMD_WRITE,1);
            memcpy(xLogHeader.xLogName,psCDC,strlen(psCDC));
            memcpy(xLogHeader.xLogNbr,psLogSeqno,strlen(psLogSeqno));
            memcpy(xLogHeader.xHostName,psHostName,strlen(psHostName));

            char sLogTextLen[10];
            sprintf(sLogTextLen,"%08d",nLength);

            nHeadLen = sizeof(struct remote_request_header);
            memcpy(lpBuff,&xLogHeader,nHeadLen);
        }
        memcpy(lpBuff+nHeadLen,psLogText,nLength);         
        iRet = send_request(lpBuff);
        if (pNewBuf)
        {
            delete []pNewBuf;
        }
        return iRet;
    }

    int socket_client::commit(const char* psCDC,const char* psLogSeqno,const char* psHostName)
    {
        if(!m_isHeader)
        {   
            return 0;
        }

        char sLogSendBuff[maxBufferSize]={0};
        char* lpBuff = sLogSendBuff;

        struct remote_request_header xLogHeader;
        memset(&xLogHeader,' ',sizeof(struct remote_request_header));

        memcpy(xLogHeader.xCommond,UDP_CMD_COMMIT,1);
        memcpy(xLogHeader.xLogName, psCDC,  strlen(psCDC));
        memcpy(xLogHeader.xLogNbr,  psLogSeqno,  strlen(psLogSeqno));
        memcpy(xLogHeader.xHostName,psHostName,strlen(psHostName));

        char sLogTextLen[10];
        sprintf(sLogTextLen,"%08d",0);
        memcpy(xLogHeader.xLength,sLogTextLen,strlen(sLogTextLen));

        memcpy(lpBuff,&xLogHeader,sizeof(struct remote_request_header));

        return send_request(lpBuff);
    }

    int socket_client::send_request(const char* psLogText)
    {
        if(m_sockFd>0)
        {
            if(sendto(m_sockFd,psLogText,strlen(psLogText),0,(struct sockaddr*)&m_sockAddr,	sizeof(m_sockAddr)) < 0)
            {
                perror("sendto error");		   
                return -1;
            }
        }
        return 0;
    } 

    int socket_client::recv_response(char* psLogText)
    {
        unsigned int fromLen;
        struct sockaddr_in fromAddr;
        fromLen = sizeof(fromAddr);
        if(recvfrom(m_sockFd,psLogText,8192,0,(struct sockaddr*)&fromAddr,&fromLen)<0)
        {
            perror("recvfrom error");
            return -1;
        }
        return 0;
    }

    logcplus_writer_remote_udp::logcplus_writer_remote_udp()
        : logcplus_base_writer(logcplus_remoteUdpAppender)
    {
        memset(m_logCdc,0x00,sizeof(m_logCdc));
        memset(m_hostName,0x00,sizeof(m_hostName));
    };

    logcplus_writer_remote_udp::~logcplus_writer_remote_udp()
    {
        LogUInit();
    }

    int  logcplus_writer_remote_udp::LogInit()
    {
        if(m_status)
        {
            LogUInit();
        }        
        gethostname(m_hostName,sizeof(m_hostName));
        int iRet = m_client.connect(m_ipaddr.c_str(),m_port);
        m_status = iRet == 0 ? true : false;
        return iRet;
    }

    int  logcplus_writer_remote_udp::LogInit(const string& sLogName, int nLogLevel,const string& subNode)
    {
        char sIpaddr[60] = {0},sPort[20] = {0};
        size_t pos = m_reserved.find(':',0);
        if(  pos == std::string::npos) return -1;
        m_reserved.copy(sIpaddr,pos,0);
        m_ipaddr = sIpaddr;
        m_reserved.copy(sPort,m_reserved.length()-pos-1,pos+1);
        m_port = std::stoi(sPort);
        m_logLevel = (logcplus_level_t)nLogLevel;
        memcpy(m_logCdc,sLogName.c_str(),sLogName.length());
        return LogInit();
    }

    int  logcplus_writer_remote_udp::LogUInit()
    {
        m_client.disConnect();
        m_status = false;
        return 0;
    }

    int  logcplus_writer_remote_udp::LogBegin(uint64_t uLogSeqno)
    {
        m_logMsgId = uLogSeqno;
        char smsgid[24] = {0};
        sprintf(smsgid,"%lu",m_logMsgId);
        return m_client.begin(m_logCdc,smsgid,m_hostName);
    }

    void logcplus_writer_remote_udp::LogCommit()
    {
        char smsgid[24] = {0};
        sprintf(smsgid,"%lu",m_logMsgId);        
        m_client.commit(m_logCdc,smsgid,m_hostName);
    }

    void logcplus_writer_remote_udp::LogWrite(const char* File, const char* Function, int Line,  int Level, const char* LogMsg)
    {
        char sLogText[maxBufferSize]={0};
        char sLogFmtBuf[1024]={0};
        logcplus_layout logLayout(m_layout.c_str());
        logLayout.formatLayout(File,Function,Line,Level,sLogFmtBuf);
        snprintf(sLogText,sizeof(sLogText)-1,"%s %s\n",sLogFmtBuf,LogMsg);
        LogWrite(sLogText,Level);
        return;
    }

    void logcplus_writer_remote_udp::LogWrite(const char* sLogText,int nFlag)
    {
        char smsgid[24] = {0};
        sprintf(smsgid,"%lu",m_logMsgId);
        m_client.write(m_logCdc,smsgid,m_hostName,sLogText);
        return;
    }

    void logcplus_writer_remote_udp::Trace(  const char* File, const char* Function, int Line,  const char* Fmt, ...)
    {
        if(!m_status) return;
        WRITE_LOG(LOGCPLUS_TRACE);
    }

    void logcplus_writer_remote_udp::Debug(  const char* File, const char* Function, int Line,  const char* Fmt, ...)
    {
        if(!m_status) return;
        WRITE_LOG(LOGCPLUS_DEBUG);
    }

    void logcplus_writer_remote_udp::Info(   const char* File, const char* Function, int Line,  const char* Fmt, ...)
    {
        if(!m_status) return;
        WRITE_LOG(LOGCPLUS_INFO);
    }

    void logcplus_writer_remote_udp::Warn(   const char* File, const char* Function, int Line,  const char* Fmt, ...)
    {
        if(!m_status) return;
        WRITE_LOG(LOGCPLUS_WARN);
    }

    void logcplus_writer_remote_udp::Error(  const char* File, const char* Function, int Line,  const char* Fmt, ...)
    {
        if(!m_status) return;
        WRITE_LOG(LOGCPLUS_ERROR);
    }

    void logcplus_writer_remote_udp::Fatal(  const char* File, const char* Function, int Line,  const char* Fmt, ...)
    {
        if(!m_status) return;
        WRITE_LOG(LOGCPLUS_FATAL);
    }

    void logcplus_writer_remote_udp::LogHex( const char* File, const char* Function, int Line,  int Level, const void* data, size_t dataSize )
    {
        if(!m_status) return;
        if(	!IsEnabledFor(Level) ) { return; }

        char sHexFmtBuf[maxBufferSize]={0};
        char sLogMsg[maxBufferSize]={0};
        char sLogFmtBuf[1024]={0};	

        FormatHex(data,"", dataSize,sHexFmtBuf);
        logcplus_layout logLayout(m_layout.c_str());
        logLayout.formatLayout(File,Function,Line,Level,sLogFmtBuf);
        snprintf(sLogMsg,sizeof(sLogMsg)-1,"%s %s\n",sLogFmtBuf,sHexFmtBuf);
        LogWrite(sLogMsg); 	
    }

    void logcplus_writer_remote_udp::Logger( const char* File, const char* Function, int Line,  int Level, const char* Fmt, ...)
    {
        if(!m_status) return;
        WRITE_LOG(Level);
    }

    void logcplus_writer_remote_udp::WLog(   const char* File, const char* Function, int Line,  int Level, const char* sLogText)
    {
        if(!m_status) return;
        LogWrite(File,Function,Line,Level,sLogText);
    }

}